type EventHandler = (...args: any) => void;

export default class WSocket {
  private instance: WebSocket;
  private events = new Map<string, EventHandler[]>();
  closed = false;
  constructor(private readonly url: string) {
    this.instance = new WebSocket(url);
    this.init();
  }
  private init() {
    this.instance.addEventListener("open", () => {
      this.handle("connected");
    });
    this.instance.addEventListener("message", (e) => {
      const { type, data } = JSON.parse(e.data);
      this.handle(type, data);
    });
    this.instance.addEventListener("error", (e) => {
      if (__DEV__) {
        console.error("websocket error");
        console.error(e);
      }
      this.handle("error", e);
      if (this.instance.readyState === WebSocket.CLOSED) {
        this.reconnect();
      }
    });
    this.instance.addEventListener("close", () => {
      this.handle("close");
      if (!this.closed) {
        this.reconnect(true);
      }
    });
  }

  private reconnect(immediate = false) {
    setTimeout(
      () => {
        this.instance = new WebSocket(this.url);
        this.init();
      },
      immediate ? 0 : 5000,
    );
  }

  private handle(event: string, ...args: any) {
    const handlers = this.events.get(event);
    if (!handlers) {
      __DEV__ && console.warn(`no events handler for event "${event}"`);
      return;
    }
    handlers.forEach((handler) => {
      handler(...args);
    });
  }

  /**
   * 监听远程推送的事件
   */
  on(event: "connected", handler: EventHandler): void;
  on(event: "close", handler: EventHandler): void;
  on(event: "error", handler: EventHandler): void;
  on(event: string, handler: EventHandler): void;
  on(event: string, handler: EventHandler) {
    if (__DEV__) {
      console.log("bind websocket event", event);
    }
    const handlers = this.events.get(event) || [];
    handlers.push(handler);
    this.events.set(event, handlers);
    // 如果链接已建立那么立即执行
    if (event === "connected" && this.instance.readyState === WebSocket.OPEN) {
      handler();
    }
  }

  /**
   * 取消监听远程推送的事件
   */
  off(event: "connected", handler: EventHandler): void;
  off(event: "close", handler: EventHandler): void;
  off(event: "error", handler: EventHandler): void;
  off(event: string, handler: EventHandler): void;
  off(event: string, handler: EventHandler) {
    let handlers = this.events.get(event);
    if (!handlers) {
      __DEV__ && console.warn(`no events handler for event "${event}"`);
      return;
    }
    if (__DEV__) {
      console.log("off websocket event", event);
    }
    handlers = handlers.filter((_handler) => _handler !== handler);
    if (handlers.length) {
      this.events.set(event, handlers);
    } else {
      this.events.delete(event);
    }
  }

  /** 向远程推送事件 */
  emit(event: string, data?: any) {
    if (this.instance.readyState !== WebSocket.OPEN) {
      console.error("websocket is not open.");
      return;
    }
    const payload: { type: string; data?: any } = { type: event };
    if (data) {
      payload.data = data;
    }
    this.instance.send(JSON.stringify(payload));
  }

  close() {
    if (__DEV__) {
      console.log("websocket connection closed.");
    }
    this.closed = true;
    /** @see https://developer.mozilla.org/zh-CN/docs/Web/API/CloseEvent#%E5%B1%9E%E6%80%A7 */
    this.instance.close(1000);
  }
}
